//! 函数指针
//! 在 Rust中，函数为一等公民。这意味着，函数自身就可以作为函数的参数和返回值使用。

/// 函数作为参数的情况
fn math(op: fn(i32, i32) -> i32, i: i32, j: i32) -> i32 {
    op(i, j)
}

/// 函数作为返回值的情况
fn true_maker(str: &str) -> fn() -> bool {
    match str {
        "true" => is_true,
        _ => is_false,
    }
}

fn sum(i: i32, j: i32) -> i32 {
    i + j
}

fn product(i: i32, j: i32) -> i32 {
    i * j
}

fn is_true() -> bool {
    true
}

fn is_false() -> bool {
    false
}

/// 在 Rust中，函数为一等公民。这意味着，函数自身就可以作为函数的参数和返回值使用。
pub fn start() {
    let a = 2;
    let b = 3;
    assert_eq!(math(sum, a, b), 5); // 这里使用了函数指针
    assert_eq!(math(product, a, b), 6);

    // 注意，即使函数签名一样，但是他们还不是同样的类型
    // #[allow(unused_mut)]是去掉variable does not need to be mutable警告
    #[allow(unused_mut)]
    let mut sum_ptr = sum;
    let product_ptr = product;

    // 这里会报错，因为他们不是相同的类型 expected fn item, found a different fn item
    // sum_ptr = product_ptr; // 必须注释才能通过编译
    assert_eq!(sum_ptr(1, 3), 4);
    assert_eq!(product_ptr(1, 3), 3);

    // 如过要一个变量指向不同的函数指针必须显示声明类型
    let mut fn_ptr: fn(i32, i32) -> i32 = sum;
    assert_eq!(fn_ptr(1, 3), 4);
    fn_ptr = product;
    assert_eq!(fn_ptr(1, 3), 3);

    // 或者
    let mut fn_ptr: Box<dyn Fn(i32, i32) -> i32> = Box::new(sum);
    assert_eq!(fn_ptr(1, 3), 4);
    fn_ptr = Box::new(product);
    assert_eq!(fn_ptr(1, 3), 3);

    // 函数做为返回值
    assert!(true_maker("true")());
    assert!(!true_maker("false")());
}
